package com.candy.common.aspectj;

import cn.hutool.core.util.StrUtil;
import com.candy.common.annotations.Lock;
import com.candy.common.exception.BizException;
import com.candy.common.utils.SpelUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁注解处理
 *
 * @author rong xi
 * @version 1.0
 * @date 2023/09/14 18:28
 */
@Slf4j
@Aspect
@Component
public class LockAspect  {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 定义注解切点
     */
    @Pointcut("@annotation(com.candy.common.annotations.Lock)")
    public void annotationPointCut() {
    }

    /**
     * 针对某个切点执行逻辑
     *
     */
    @Around("annotationPointCut()")
    public Object lock(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        Lock lock = method.getAnnotation(Lock.class);
        //若无该注解，则不实用分布式锁
        if (lock == null) {
            return proceedingJoinPoint.proceed();
        }
        String value = lock.value();
        String id = StrUtil.isBlank(lock.key())? "":":"+ SpelUtil.getKeyValue(lock,proceedingJoinPoint);
        if (StrUtil.isBlank(value)) {
            //方法名
            String methodName = method.getName();
            //类名
            String className = proceedingJoinPoint.getTarget().getClass().getName();
            value = className + "." + methodName;
        }

        RLock rLock = redissonClient.getLock(value + id);
        try {
            if (rLock.tryLock( lock.waitTime(),lock.leaseTime(), TimeUnit.MILLISECONDS)) {
                return proceedingJoinPoint.proceed();
            }
        } catch (Exception e) {
            log.error("分布式锁异常:", e);
            throw e;
        }finally {
            // 是否还是锁定状态
            if (rLock.isLocked()) {
                // 时候是当前执行线程的锁
                if (rLock.isHeldByCurrentThread()) {
                    // 释放锁
                    rLock.unlock();
                }
            }
        }
        throw new BizException("500","操作频繁");
    }
}
